<?php

namespace App\Http\Controllers\Payment;

use App\Http\Controllers\Controller;
use App\Models\Invoice\Invoice;
use App\Models\Invoice\Subject;
use App\Models\Payment\PaymentChannel;
use App\Models\Payment\PaymentOrder;
use App\Models\Payment\Platform\ChinaUms;
use App\Models\Invoice\Platform\ChinaUms as InvoiceChinaUms;
use App\Models\Payment\Platform\EbankPay;
use App\Models\Payment\Platform\FuiouAggregatePay;
use App\Models\Payment\Platform\FuiouPay;
use App\Models\Payment\Platform\UnionPay;
use Carbon\Carbon;
use EasyWeChat\Kernel\Support\XML;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;

class NotifyController extends Controller
{
    public function paid(Request $request, string $payType, string $paymentChannelID)
    {
        $payType = strtoupper($payType);
        $logName = "{$payType}:{$paymentChannelID}";
        Log::channel('payment-notify')->info($logName, ['request' => file_get_contents("php://input") ?: $request->all(), 'headers' => $request->headers->all()]);

        switch (strtoupper($payType)) {
            case PaymentChannel::CATEGORY_CHINAUMS:

                if (!$paymentChannel = PaymentChannel::find($paymentChannelID)) {
                    Log::channel('payment-notify-error')->info($logName, ['err' => '非法的支付通道ID']);
                    return ChinaUms::NOTIFY_CODE_SUCCESS;
                }

                try {

                    if (!is_array($inputs = $request->all())) {
                        throw new \Exception('回调参数数据类型异常');
                    }

                    $notifyDat = ChinaUms::notifyDatFilter($inputs);

                    if (($merOrderID = Arr::get($notifyDat, 'merOrderId')) &&
                        ($paymentOrder = PaymentOrder::where('paid_code', $merOrderID)->first())) {

                        return (new ChinaUms($paymentChannel))->handleNotify($paymentOrder, $notifyDat);
                    }
                } catch (\Throwable $exception) {
                    Log::channel('payment-notify-error')->info($logName, ['err' => $exception->getMessage()]);
                }
                return ChinaUms::NOTIFY_CODE_FAIL;
            case PaymentChannel::CATEGORY_UNIONPAY_APP:
            case PaymentChannel::CATEGORY_UNIONPAY_ALIPAY:
            case PaymentChannel::CATEGORY_UNIONPAY_WECHATPAY:
            case PaymentChannel::CATEGORY_UNIONPAY_WECHATPAY_MINI:

                if (!$paymentChannel = PaymentChannel::find($paymentChannelID)) {
                    Log::channel('payment-notify-error')->info($logName, ['err' => '非法的支付通道ID']);
                    return UnionPay::NOTIFY_CODE_SUCCESS;
                }

                try {
                    if (!is_string($inputs = file_get_contents("php://input"))) {
                        throw new \Exception('回调参数数据类型异常');
                    }

                    if (!$notifyDat = XML::parse($inputs)) {
                        throw new \Exception('未获取到回调参数');
                    }

                    if (($paidCode = Arr::get($notifyDat, 'out_trade_no')) &&
                        ($paymentOrder = PaymentOrder::where('paid_code', $paidCode)->first())) {
                        return (new UnionPay($paymentChannel))->handleNotify($paymentOrder, $notifyDat);
                    }

                } catch (\Throwable $exception) {
                    Log::channel('payment-notify-error')->info($logName, ['err' => $exception->getMessage()]);
                }
                return UnionPay::NOTIFY_CODE_FAIL;
            case PaymentChannel::CATEGORY_FUIOUPAY:

                if (!$paymentChannel = PaymentChannel::find($paymentChannelID)) {
                    Log::channel('payment-notify-error')->info($logName, ['err' => '非法的支付通道ID']);
                    return FuiouPay::NOTIFY_CODE_SUCCESS;
                }

                try {
                    if (!is_string($inputs = $request->get('req'))) {
                        throw new \Exception('回调参数数据类型异常');
                    }

                    // 解码得到xml
                    $xmlDat = urldecode(urldecode($inputs));
                    if (!$notifyDat = xml2Arr($xmlDat)) {
                        throw new \Exception('未获取到回调参数');
                    }

                    if (($paidCode = Arr::get($notifyDat, 'mchnt_order_no')) &&
                        ($paymentOrder = PaymentOrder::where('paid_code', $paidCode)->first())) {
                        return (new FuiouPay($paymentChannel))->handleNotify($paymentOrder, $notifyDat);
                    }

                } catch (\Throwable $exception) {
                    Log::channel('payment-notify-error')->info($logName, ['err' => $exception->getMessage()]);
                }
                return FuiouPay::NOTIFY_CODE_FAIL;
            case PaymentChannel::CATEGORY_FUIOUPAY_AGGREGATE:

                if (!$paymentChannel = PaymentChannel::find($paymentChannelID)) {
                    Log::channel('payment-notify-error')->info($logName, ['err' => '非法的支付通道ID']);
                    return FuiouPay::NOTIFY_CODE_SUCCESS;
                }

                try {
                    if (!is_array($inputs = $request->all())) {
                        throw new \Exception('回调参数数据类型异常');
                    }

                    if (($paidCode = Arr::get($inputs, 'mchnt_order_no')) &&
                        ($paymentOrder = PaymentOrder::where('paid_code', $paidCode)->first())) {
                        return (new FuiouAggregatePay($paymentChannel))->handleNotify($paymentOrder, $inputs);
                    }

                } catch (\Throwable $exception) {
                    Log::channel('payment-notify-error')->info($logName, ['err' => $exception->getMessage()]);
                }
                return FuiouPay::NOTIFY_CODE_FAIL;
            case PaymentChannel::CATEGORY_EBANKPAY:

                if (!$paymentChannel = PaymentChannel::find($paymentChannelID)) {
                    Log::channel('payment-notify-error')->info($logName, ['err' => '非法的支付通道ID']);
                    return EbankPay::NOTIFY_CODE_SUCCESS;
                }

                try {
                    $notifyDat = [
                        'body' => $request->getContent(),
                        'headers' => $request->headers->all()
                    ];

                    $result = (new EbankPay($paymentChannel))->decryptResponse($notifyDat);
                    if (1 !== Arr::get($result, 'code')) {
                        Log::channel('payment-notify-error')->info($logName, ['err' => '支付回调验签失败', 'notifyDat' => $notifyDat, 'check_result' => $result]);
                        return EbankPay::NOTIFY_CODE_FAIL;
                    }
                    Log::channel('payment-notify')->info($logName . '报文解密', $result);

                    $notifyDat = Arr::get($result, 'data');
                    if (($paidCode = Arr::get($notifyDat, 'mchntOrderId')) &&
                        ($paymentOrder = PaymentOrder::where('paid_code', $paidCode)->first())) {
                        return (new EbankPay($paymentChannel))->handleNotify($paymentOrder, $notifyDat);
                    }

                } catch (\Throwable $exception) {
                    Log::channel('payment-notify-error')->info($logName, ['err' => $exception->getMessage()]);
                }
                return EbankPay::NOTIFY_CODE_FAIL;
            default:
                return UnionPay::NOTIFY_CODE_SUCCESS;
        }
    }

    public function invoice(Request $request, string $type, string $act)
    {
        $type = strtoupper($type);
        $act = strtolower($act);
        $invoiceDat = $request->all();

        Log::channel('invoice')->info('开票异步回调', ['request' => $request->all() ?: file_get_contents("php://input")]);

        switch ($type) {
            case Subject::TYPE_CHINA_UMS:
                if ($act === 'issuance') {

                    $status = \Arr::get($invoiceDat, 'status');
                    $relatedCode = \Arr::get($invoiceDat, 'merOrderId');

                    if (($status === InvoiceChinaUms::STATUS_ISSUED) && $relatedCode) {

                        $invoicePacket = Invoice::where('related_code', $relatedCode)->where('type', Invoice::TYPE_ISSUED)->orderBy('updated_at', "DESC")->get();

                        switch ($invoicePacket->count()) {
                            case 0:
                                $invoice = null;
                                break;
                            case 1:
                                $invoice = $invoicePacket->first();
                                break;
                            default:
                                if (!$invoice = $invoicePacket->where('status', Invoice::STATUS_SUCCESS)->first()) {
                                    $invoice = $invoicePacket->first();
                                }
                                break;
                        }

                        if ($invoice) {

                            $xmlDat = $invoice->xml_dat;
                            $invoice->xml_dat = [
                                'xml' => \Arr::get($xmlDat, 'xml') ?: strval(\Arr::get($invoiceDat, 'xml')),
                                'preview_url' => \Arr::get($xmlDat, 'preview_url') ?: strval(\Arr::get($invoiceDat, 'xmlPreviewUrl')),
                                'url' => \Arr::get($xmlDat, 'url') ?: strval(\Arr::get($invoiceDat, 'xmlUrl'))
                            ];

                            $pdfDat = $invoice->pdf_dat;
                            $invoice->pdf_dat = [
                                'pdf' => \Arr::get($pdfDat, 'pdf') ?: strval(\Arr::get($invoiceDat, 'pdf')),
                                'preview_url' => \Arr::get($pdfDat, 'preview_url') ?: strval(\Arr::get($invoiceDat, 'pdfPreviewUrl')),
                                'url' => \Arr::get($pdfDat, 'url') ?: strval(\Arr::get($invoiceDat, 'pdfUrl'))
                            ];

                            $ofdDat = $invoice->ofd_dat;
                            $invoice->ofd_dat = [
                                'ofd' => \Arr::get($ofdDat, 'ofd') ?: strval(\Arr::get($invoiceDat, 'ofd')),
                                'preview_url' => \Arr::get($ofdDat, 'preview_url') ?: strval(\Arr::get($invoiceDat, 'ofdPreviewUrl')),
                                'url' => \Arr::get($ofdDat, 'url') ?: strval(\Arr::get($invoiceDat, 'ofdUrl'))
                            ];

                            $qrCodeDat = $invoice->qr_code_dat;
                            $invoice->qr_code_dat = [
                                'qr_code' => \Arr::get($qrCodeDat, 'qr_code') ?: strval(\Arr::get($invoiceDat, 'qrCodeId')),
                                'preview_url' => \Arr::get($qrCodeDat, 'preview_url') ?: strval(\Arr::get($invoiceDat, 'qrCode')),
                                'url' => \Arr::get($qrCodeDat, 'url') ?: strval(\Arr::get($invoiceDat, 'issueQrCode'))
                            ];

                            $invoice->amount = floatval(\Arr::get($invoiceDat, 'totalPrice'));
                            $invoice->total_amount = floatval(\Arr::get($invoiceDat, 'totalPriceIncludingTax'));
                            $invoice->tax_amount = floatval(\Arr::get($invoiceDat, 'totalTax'));

                            try {
                                $issueDate = Carbon::parse(\Arr::get($invoiceDat, 'issueDate'))->toDateTimeString();
                            } catch (\Throwable $exception) {
                                $issueDate = now()->toDateTimeString();
                            }
                            $invoice->triggered_at = $invoice->triggered_at ?: $issueDate;
                            $invoice->cipher_code = $invoice->cipher_code ?: strval(\Arr::get($invoiceDat, 'cipherCode'));
                            $invoice->check_code = $invoice->check_code ?: strval(\Arr::get($invoiceDat, 'checkCode'));
                            $invoice->good_details = $invoice->good_details ?: @json_decode(strval(\Arr::get($invoiceDat, 'goodsDetail')), true) ?: [];
                            $invoice->code = $invoice->code ?: strval(\Arr::get($invoiceDat, 'invoiceCode'));
                            $invoice->number = $invoice->number ?: strval(\Arr::get($invoiceDat, 'invoiceNo'));
                            $invoice->status = Invoice::STATUS_SUCCESS;

                            $invoice->save();
                        }
                    }
                }
                break;
            default:
                break;
        }
    }
}
